<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>LLM 聊天界面 (完整版)</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <style>
        /* CSS 变量定义 */
        :root {
            --primary-color: #2c3e50;
            --user-bubble: #409EFF;
            --user-bubble-text: #ffffff;
            --bot-bubble: #e9e9eb;
            --bot-bubble-text: #303133;
            --error-bubble: #fef0f0;
            --error-text: #f56c6c;
            --background: #f0f2f5;
            --sidebar-bg: #202123;
            --sidebar-hover-bg: #343541;
            --header-height: 60px;
            --footer-height: 70px;
        }

        /* 基础样式重置 */
        * { margin: 0; padding: 0; box-sizing: border-box; }

        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
            background-color: var(--background);
            height: 100vh;
            overflow: hidden;
        }

        /* 主布局 */
        .main-layout { display: flex; height: 100vh; }

        /* 侧边栏样式 */
        .sidebar {
            width: 260px;
            background-color: var(--sidebar-bg);
            color: #ececf1;
            display: flex;
            flex-direction: column;
            padding: 10px;
            flex-shrink: 0;
        }
        .new-chat-btn {
            display: flex; align-items: center; justify-content: center; gap: 10px;
            width: 100%; padding: 12px; border: 1px solid rgba(255, 255, 255, 0.3);
            border-radius: 6px; background-color: transparent; color: #ececf1;
            cursor: pointer; font-size: 14px; transition: background-color 0.2s;
        }
        .new-chat-btn:hover { background-color: var(--sidebar-hover-bg); }
        .history-list {
            flex-grow: 1; overflow-y: auto; margin-top: 20px; padding-right: 5px;
        }
        .history-list::-webkit-scrollbar { width: 6px; }
        .history-list::-webkit-scrollbar-thumb { background: #555; border-radius: 3px; }
        .history-list::-webkit-scrollbar-thumb:hover { background: #777; }
        .history-list ul { list-style: none; }
        .history-list li a {
            display: block; text-decoration: none; color: #ececf1; padding: 10px 15px;
            border-radius: 6px; white-space: nowrap; overflow: hidden;
            text-overflow: ellipsis; transition: background-color 0.2s;
        }
        .history-list li a.active, .history-list li a:hover { background-color: var(--sidebar-hover-bg); }
        .user-profile { border-top: 1px solid rgba(255, 255, 255, 0.2); padding-top: 15px; position: relative; }
        .user-info { display: flex; align-items: center; padding: 10px; border-radius: 6px; cursor: pointer; transition: background-color 0.2s; }
        .user-info:hover { background-color: var(--sidebar-hover-bg); }
        .user-info .avatar {
            width: 32px; height: 32px; border-radius: 50%; background-color: var(--user-bubble);
            color: white; display: flex; align-items: center; justify-content: center;
            font-weight: bold; margin-right: 10px; flex-shrink: 0;
        }
        .user-info .username { font-weight: 500; flex-grow: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        .settings-menu {
            position: absolute; bottom: calc(100% + 5px); left: 0; width: 100%;
            background-color: #343541; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.3);
            padding: 8px; z-index: 100; display: none;
        }
        .settings-menu.show { display: block; }
        .settings-menu a { display: flex; align-items: center; gap: 10px; padding: 10px; color: #ececf1; text-decoration: none; border-radius: 5px; }
        .settings-menu a:hover { background-color: #555869; }
        .settings-menu a i { width: 16px; text-align: center; }

        /* 聊天容器样式 */
        .chat-container { flex-grow: 1; height: 100%; display: flex; flex-direction: column; background: white; }
        .chat-header {
            height: var(--header-height); background-color: var(--primary-color); color: white;
            display: flex; justify-content: space-between; align-items: center; padding: 0 20px; flex-shrink: 0;
        }
        .chat-header h1 { font-size: 1.25rem; font-weight: bold; }
        .header-controls { display: flex; align-items: center; gap: 15px; }
        .clear-btn { background: none; border: none; color: white; cursor: pointer; font-size: 14px; padding: 5px 10px; border-radius: 4px; display: flex; align-items: center; gap: 5px; }
        .clear-btn:hover { background: rgba(255, 255, 255, 0.1); }
        .mode-select { width: 120px; padding: 8px 12px; border-radius: 4px; border: none; background: rgba(255, 255, 255, 0.15); color: white; cursor: pointer; }
        .mode-select option { background: white; color: #333; }

        /* --- 新增：聊天消息区域样式 --- */
        .chat-messages { flex: 1; overflow-y: auto; padding: 20px; background-color: #fcfcfc; }
        .message-row { display: flex; margin-bottom: 15px; max-width: 80%; }
        .message-row .avatar {
            width: 36px; height: 36px; border-radius: 50%; background-color: #ccc;
            display: flex; align-items: center; justify-content: center;
            margin-right: 15px; flex-shrink: 0; font-size: 16px;
        }
        .message-row.bot .avatar { background-color: var(--primary-color); color: white; }
        .message-row.error .avatar { background-color: var(--error-text); color: white; }
        .message-bubble { padding: 12px 16px; border-radius: 18px; line-height: 1.6; word-wrap: break-word; }
        .message-bubble.user { background-color: var(--user-bubble); color: var(--user-bubble-text); border-top-right-radius: 4px; }
        .message-bubble.bot { background-color: var(--bot-bubble); color: var(--bot-bubble-text); border-top-left-radius: 4px; }
        .message-bubble.error { background-color: var(--error-bubble); color: var(--error-text); border: 1px solid var(--error-text); border-top-left-radius: 4px; }
        .message-row.user { margin-left: auto; flex-direction: row-reverse; }
        .message-row.user .message-bubble { text-align: left; }
        /* Typing Indicator */
        .typing-indicator { display: flex; align-items: center; justify-content: center; }
        .typing-indicator .dot { width: 8px; height: 8px; margin: 0 3px; background-color: #aaa; border-radius: 50%; animation: typing 1.4s infinite; }
        .typing-indicator .dot:nth-child(2) { animation-delay: 0.2s; }
        .typing-indicator .dot:nth-child(3) { animation-delay: 0.4s; }
        @keyframes typing { 0%, 80%, 100% { transform: scale(0); } 40% { transform: scale(1.0); } }

        /* 聊天输入区域样式 */
        .chat-input-area {
            padding: 10px 20px; background-color: #ffffff; border-top: 1px solid #e0e0e0;
            display: flex; align-items: center; flex-shrink: 0; min-height: var(--footer-height);
        }
        .input-wrapper {
            width: 100%; display: flex; align-items: flex-end; background: #f4f6f8;
            border: 1px solid #dcdfe6; border-radius: 20px; padding: 8px 10px 8px 15px;
            transition: border-color 0.2s, box-shadow 0.2s;
        }
        .input-wrapper:focus-within { border-color: var(--user-bubble); box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2); }
        #userInput {
            flex-grow: 1; border: none; outline: none; resize: none; font-family: inherit;
            font-size: 16px; line-height: 1.5; max-height: 120px; overflow-y: auto;
            background: transparent; color: #303133;
        }
        #userInput::placeholder { color: #a8abb2; }
        #sendBtn {
            background-color: var(--user-bubble); color: white; border: none; border-radius: 50%;
            width: 34px; height: 34px; font-size: 16px; cursor: pointer; display: flex;
            align-items: center; justify-content: center; flex-shrink: 0; margin-left: 10px;
            transition: background-color 0.2s, opacity 0.2s;
        }
        #sendBtn:hover { background-color: #337ecc; }
        #sendBtn:disabled { background-color: #a0cfff; cursor: not-allowed; opacity: 0.7; }

        /* 文件上传按钮样式 */
        #attachBtn {
            background-color: #67c23a; color: white; border: none; border-radius: 50%;
            width: 34px; height: 34px; font-size: 16px; cursor: pointer; display: flex;
            align-items: center; justify-content: center; flex-shrink: 0; margin-left: 5px;
            transition: background-color 0.2s, opacity 0.2s;
        }
        #attachBtn:hover { background-color: #529b2e; }
        #attachBtn:disabled { background-color: #a4da7a; cursor: not-allowed; opacity: 0.7; }

    </style>
</head>
<body>

    <div class="main-layout">
        <div class="sidebar">
            <button class="new-chat-btn" id="newChatBtn"><i class="fa-solid fa-plus"></i> 新建聊天</button>
            <div class="history-list">
                <ul>
                    <li><a href="#" class="active">今天：关于Python的有趣事实</a></li>
                    <li><a href="#">昨天：如何规划一次完美的旅行</a></li>
                </ul>
            </div>
            <div class="user-profile">
                <div id="settings-menu" class="settings-menu">
                    <a href="#"><i class="fa-solid fa-gear"></i> 系统设置</a>
                    <a href="#" id="logout-link"><i class="fa-solid fa-right-from-bracket"></i> 退出登录</a>
                </div>
                <div class="user-info" id="user-info-trigger">
                    <div class="avatar">U</div>
                    <span class="username">用户名</span>
                </div>
            </div>
        </div>

        <div class="chat-container">
            <header class="chat-header">
                <h1>LLM Chat</h1>
                <div class="header-controls">
                    <button id="clearBtn" class="clear-btn"><i class="fa-solid fa-trash-can"></i><span>清空</span></button>
                    <select id="chatType" class="mode-select">
                        <option value="gpt-4">知识库</option>
                        <option value="gpt-3.5-turbo">GPT-3.5</option>
                    </select>
                </div>
            </header>

            <main id="chatMessages" class="chat-messages"></main>

            <footer class="chat-input-area">
                <div class="input-wrapper">
                    <textarea id="userInput" placeholder="输入消息... (Shift + Enter 换行)" rows="1"></textarea>
                    <input type="file" id="fileInput" style="display:none" accept=".pdf,.doc,.docx" />
                    <button id="attachBtn" title="上传文件" type="button">
                        <i class="fa-solid fa-paperclip"></i>
                    </button>
                    <button id="sendBtn" title="发送消息" disabled>
                        <i class="fa-solid fa-paper-plane"></i>
                    </button>
                </div>
            </footer>
        </div>
    </div>


<script>
        document.addEventListener('DOMContentLoaded', function() {
            // --- DOM Element Selection ---
            const chatMessages = document.getElementById('chatMessages');
            const userInput = document.getElementById('userInput');
            const sendBtn = document.getElementById('sendBtn');
            const attachBtn = document.getElementById('attachBtn');
            const fileInput = document.getElementById('fileInput');
            const clearBtn = document.getElementById('clearBtn');
            const newChatBtn = document.getElementById('newChatBtn');
            const historyList = document.querySelector('.history-list ul');
            const usernameSpan = document.querySelector('.username');
            const avatarDiv = document.querySelector('.avatar');
            const userInfoTrigger = document.getElementById('user-info-trigger');
            const settingsMenu = document.getElementById('settings-menu');
            const logoutLink = document.getElementById('logout-link');

            // --- State Management ---
            let conversations = JSON.parse(localStorage.getItem('conversations') || '[]');
            let conversationId = null;
            let messages = [];
            let isLoading = false;

            // --- UI Interaction Logic ---
            userInfoTrigger.addEventListener('click', (event) => {
                event.stopPropagation();
                settingsMenu.classList.toggle('show');
            });
            window.addEventListener('click', () => settingsMenu.classList.remove('show'));
            settingsMenu.addEventListener('click', (event) => event.stopPropagation());
            userInput.addEventListener('input', () => {
                userInput.style.height = 'auto';
                userInput.style.height = (userInput.scrollHeight) + 'px';
                updateSendDisabled();
            });

            // --- Core Functions ---
            function updateSendDisabled() {
                sendBtn.disabled = isLoading || (!userInput.value.trim() && fileInput.files.length === 0);
                attachBtn.disabled = isLoading;
            }

            function scrollToBottom() {
                setTimeout(() => { chatMessages.scrollTop = chatMessages.scrollHeight; }, 10);
            }

            // --- Conversation Management ---
            function saveConversations() {
                localStorage.setItem('conversations', JSON.stringify(conversations));
            }

            function renderHistory() {
                historyList.innerHTML = '';
                conversations.forEach(conv => {
                    const li = document.createElement('li');
                    const a = document.createElement('a');
                    a.href = '#';
                    a.textContent = conv.title;
                    if (conv.id === conversationId) a.classList.add('active');
                    a.addEventListener('click', (e) => {
                        e.preventDefault();
                        switchConversation(conv.id);
                    });
                    li.appendChild(a);
                    historyList.appendChild(li);
                });
            }

            function switchConversation(id) {
                conversationId = id;
                const conv = conversations.find(c => c.id === id);
                messages = conv ? conv.messages : [];
                renderHistory();
                renderMessages();
                scrollToBottom();
            }

            function createConversation() {
                const id = Date.now().toString();
                const conv = {
                    id,
                    title: '新的聊天',
                    messages: [{ sender: 'bot', text: '您好！欢迎使用 LLM 聊天机器人。请选择一个聊天模式，然后开始提问吧！' }]
                };
                conversations.unshift(conv);
                saveConversations();
                switchConversation(id);
            }

            function clearChat() {
                const conv = conversations.find(c => c.id === conversationId);
                if (conv) {
                    conv.messages = [{ sender: 'bot', text: '您好！欢迎使用 LLM 聊天机器人。请选择一个聊天模式，然后开始提问吧！' }];
                    saveConversations();
                    switchConversation(conversationId);
                }
            }

            // --- Message Rendering ---
            function addMessage(sender, text) {
                messages.push({ sender, text });
                const conv = conversations.find(c => c.id === conversationId);
                if (conv && (!conv.title || conv.title === '新的聊天') && sender === 'user') {
                    conv.title = text.slice(0, 30);
                    renderHistory();
                }
                saveConversations();
                renderMessages();
                scrollToBottom();
            }

            function renderMessages() {
                chatMessages.innerHTML = '';
                messages.forEach(msg => {
                    const messageRow = document.createElement('div');
                    messageRow.className = `message-row ${msg.sender}`;

                    const bubble = document.createElement('div');
                    bubble.className = `message-bubble ${msg.sender}`;

                    if (msg.sender !== 'user') {
                        const avatar = document.createElement('div');
                        avatar.className = 'avatar';
                        const icon = document.createElement('i');
                        icon.className = msg.sender === 'bot' ? 'fas fa-comment-dots' : 'fas fa-exclamation-triangle';
                        avatar.appendChild(icon);
                        messageRow.appendChild(avatar);
                    }

                    // **FIX 1**: Added 'message-text' class to the content div
                    const content = document.createElement('div');
                    content.className = 'message-text';
                    content.textContent = msg.text;
                    bubble.appendChild(content);

                    messageRow.appendChild(bubble);
                    chatMessages.appendChild(messageRow);
                });

                if (isLoading && messages[messages.length - 1]?.sender !== 'bot') {
                    // This logic is simplified as the placeholder is now handled by streamAndRenderResponse
                }
            }

            // --- Streaming Logic ---
            async function streamAndRenderResponse(text) {
                addMessage('bot', ''); // Add empty bot message placeholder

                try {
                    const response = await fetch('/api/submit', {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({ input: text })
                    });

                    if (!response.ok) throw new Error(`服务器错误: ${response.status}`);
                    if (!response.body) throw new Error("响应体不存在");

                    const reader = response.body.getReader();
                    const decoder = new TextDecoder();
                    let fullBotResponse = "";

                    while (true) {
                        const { done, value } = await reader.read();
                        if (done) break;
                        const chunk = decoder.decode(value, { stream: true });
                        fullBotResponse += chunk;
                        appendChunkToLastMessage(chunk);
                        scrollToBottom();
                    }

                    const lastMessage = messages[messages.length - 1];
                    if (lastMessage && lastMessage.sender === 'bot') {
                        lastMessage.text = fullBotResponse;
                        saveConversations();
                    }

                } catch (error) {
                    updateLastErrorBotMessage('请求失败: ' + error.message);
                    const lastMessage = messages[messages.length - 1];
                    if (lastMessage && lastMessage.sender === 'bot') {
                        lastMessage.sender = 'error';
                        lastMessage.text = '请求失败: ' + error.message;
                        saveConversations();
                        renderMessages(); // Re-render to show the error style
                    }
                }
            }

            function appendChunkToLastMessage(chunk) {
                // **FIX 2**: Corrected DOM selector
                const messageElements = document.querySelectorAll('.message-row.bot');
                if (messageElements.length > 0) {
                    const lastBotMessage = messageElements[messageElements.length - 1];
                    const textElement = lastBotMessage.querySelector('.message-text');
                    if (textElement) {
                        textElement.textContent += chunk;
                    }
                }
            }

            function updateLastErrorBotMessage(errorMessage) {
                const messageElements = document.querySelectorAll('.message-row.bot');
                if (messageElements.length > 0) {
                    const lastBotMessage = messageElements[messageElements.length - 1];
                    const textElement = lastBotMessage.querySelector('.message-text');
                    if (textElement) {
                        textElement.textContent = errorMessage;
                        lastBotMessage.parentElement.classList.remove('bot');
                        lastBotMessage.parentElement.classList.add('error');
                    }
                }
            }

            // --- Main Action Handlers ---
            async function sendMessage() {
                const text = userInput.value.trim();
                if (isLoading || !text) return;

                addMessage('user', text);
                userInput.value = '';
                userInput.style.height = 'auto';

                isLoading = true;
                updateSendDisabled();

                try {
                    await streamAndRenderResponse(text);
                } finally {
                    isLoading = false;
                    updateSendDisabled();
                }
            }

            async function uploadFile(file) {
                if (!file) return;

                const allowedTypes = ['.pdf', '.doc', '.docx'];
                const fileExt = '.' + file.name.split('.').pop().toLowerCase();
                if (!allowedTypes.includes(fileExt)) {
                    addMessage('error', `不支持的文件类型：${fileExt}`);
                    return;
                }
                const maxSize = 10 * 1024 * 1024;
                if (file.size > maxSize) {
                    addMessage('error', `文件大小超过限制（最大10MB）`);
                    return;
                }

                isLoading = true;
                updateSendDisabled();

                const formData = new FormData();
                formData.append('file', file);

                try {
                    const uploadResponse = await fetch('/api/upload', {
                        method: 'POST',
                        body: formData
                    });
                    const data = await uploadResponse.json();

                    if (data.success) {
                        const extractedText = data.text_content;
                        if (extractedText && extractedText.trim()) {
                            addMessage('bot', `已成功提取文件内容：${data.filename}`);
                            addMessage('user', `[文件内容] ${data.filename}`);
                            await streamAndRenderResponse(extractedText);
                        } else {
                            addMessage('error', '文件中未找到可提取的文本内容');
                        }
                    } else {
                        addMessage('error', data.detail || '文件上传失败');
                    }
                } catch (error) {
                    addMessage('error', '文件上传失败: ' + error.message);
                } finally {
                    isLoading = false;
                    updateSendDisabled();
                }
            }

            // --- Event Listeners ---
            sendBtn.addEventListener('click', sendMessage);
            userInput.addEventListener('keypress', (e) => {
                if (e.key === 'Enter' && !e.shiftKey && !sendBtn.disabled) {
                    e.preventDefault();
                    sendMessage();
                }
            });
            attachBtn.addEventListener('click', () => fileInput.click());
            fileInput.addEventListener('change', () => {
                if (fileInput.files.length > 0) {
                    sendMessage(); // Auto-send after file selection
                }
            });
            clearBtn.addEventListener('click', clearChat);
            newChatBtn.addEventListener('click', createConversation);
            logoutLink.addEventListener('click', (e) => {
                e.preventDefault();
                localStorage.removeItem('username');
                window.location.href = '/login';
            });

            // --- Initialization ---
            const savedUsername = localStorage.getItem('username');
            if (savedUsername) {
                usernameSpan.textContent = savedUsername;
                avatarDiv.textContent = savedUsername.charAt(0).toUpperCase();
            } else {
                // In a real app, you might redirect: window.location.href = '/login';
                console.log("未找到用户名，请先登录。");
            }

            if (conversations.length === 0) {
                createConversation();
            } else {
                switchConversation(conversations[0].id);
            }
            updateSendDisabled();
        });
    </script>
</body>
</html>